package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.guigu.common.result.ResultCodeEnum;
import com.atguigu.guigu.common.utils.Assert;
import com.atguigu.service.base.utils.*;
import com.atguigu.srb.core.hfb.CoreUtils;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendMapper;
import com.atguigu.srb.core.pojo.entity.vo.BorrowInfoApprovalVO;
import com.atguigu.srb.core.pojo.entity.vo.BorrowerDetailVO;
import com.atguigu.srb.core.pojo.entity.vo.LendIndexSearchVO;
import com.atguigu.srb.core.service.*;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-11-19
 */
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {
    @Resource
    BorrowInfoService borrowInfoService;
    @Resource
    DictService dictService;
    @Resource
    UserAccountService userAccountService;
    @Resource
    LendItemService lendItemService;
    @Resource
    BorrowerService borrowerService;
    @Resource
    TransFlowService transFlowService;
    @Resource
    UserInfoService userInfoService;
    @Resource
    LendReturnService lendReturnService;
    @Resource
    LendItemReturnService lendItemReturnService;
    public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, String borrowId) {
        BorrowInfo borrowInfo = borrowInfoService.getById(borrowId);
        Lend lend = new Lend();
        lend.setUserId(borrowInfo.getUserId());
        lend.setBorrowInfoId(borrowInfo.getId());
        //通过工具类生成
        lend.setLendNo(LendNoUtils.getLendNo());
        lend.setTitle(borrowInfoApprovalVO.getTitle());
        lend.setAmount(borrowInfo.getAmount());
        lend.setPeriod(borrowInfo.getPeriod());
        lend.setLendYearRate(borrowInfoApprovalVO.getLendYearRate().divide(new BigDecimal("100")));
        lend.setServiceRate(borrowInfoApprovalVO.getServiceRate().divide(new BigDecimal("100")));
        lend.setReturnMethod(borrowInfo.getReturnMethod());
        lend.setLowestAmount(new BigDecimal("100"));
        lend.setInvestAmount(new BigDecimal("0"));
        lend.setRealAmount(new BigDecimal("0"));
        lend.setInvestNum(0);
        lend.setLendStartDate(borrowInfoApprovalVO.getLendStartDate());

        //利息结束日：还款起息日+还款期限
        Date date = new DateTime(borrowInfoApprovalVO.getLendStartDate())
                .plusMonths(borrowInfo.getPeriod()).toDate();
        lend.setLendEndDate(date);
        lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());
        //预期的收益： 月利率*期数
        //月利率：
        BigDecimal lendMonthRate = lend.getLendYearRate().divide(new BigDecimal("12"), 8,
                BigDecimal.ROUND_DOWN);//年利率%12 保留小数点后8位  超出的忽略

        //期望收益：月利率*期数*借款金额
        lend.setExpectAmount(lendMonthRate.multiply(new BigDecimal(borrowInfo.getPeriod())).multiply(borrowInfo.getAmount()));//年利率转为月利率*期数
            /*
            CHECK(0, "待发布"),
            INVEST_RUN(1, "募资中"),
            PAY_RUN(2, "还款中"),
            PAY_OK(3, "已结清"),
            FINISH(4, "结标"),
            CANCEL(-1, "已撤标"),
            ;
             */
        lend.setStatus(0);
        lend.setCheckTime(new Date());
        lend.setCheckAdminId(1L);
        this.save(lend);
    }

    @Override
    public Map<String, Object> getLend(String id) {
        //获取lend对象
        Lend lend = this.getById(id);
        Integer status = lend.getStatus();
        String statusStr = getStatusString(status);
        lend.getParam().put("status", statusStr);
        String name = dictService.getDictNameByDictCodeAndValue("returnMethod", lend.getReturnMethod());
        lend.getParam().put("returnMethod", name);
        //获取借款人对象
        Borrower borrower = borrowerService.getOne(Wrappers.lambdaQuery(Borrower.class)
                .eq(Borrower::getUserId, lend.getUserId())
                .select(Borrower::getId));
        BorrowerDetailVO borrowerDetailVO = borrowerService.getBorrowerDetail(borrower.getId().toString());

        Map<String, Object> map = new HashMap<>();
        map.put("lend", lend);
        map.put("borrowerDetailVO", borrowerDetailVO);
        return map;
    }

    public String getStatusString(Integer status) {
        String statusStr = "";
        switch (status) {
            case 0:
                statusStr = "待发布";
                break;
            case 1:
                statusStr = "募资中";
                break;
            case 2:
                statusStr = "还款中";
                break;
            case 3:
                statusStr = "已结清";
                break;
            case 4:
                statusStr = "结标";
                break;
            case -1:
                statusStr = "已撤标";
                break;

        }
        return statusStr;
    }

    @Override
    public List<Lend> getLendsByCondition(LendIndexSearchVO searchVO) {
        LambdaQueryWrapper<Lend> queryWrapper = Wrappers.lambdaQuery(Lend.class);
        List<Integer> lendType = searchVO.getLendType();
        List<Integer> lendYearRate = searchVO.getLendYearRate();
        List<Integer> period = searchVO.getPeriod();
        List<Integer> returnMethod = searchVO.getReturnMethod();
        //判断标的类型查询条件
        if (!CollectionUtils.isEmpty(lendType)) {
            //使用条件：1:车易贷  2:房易贷 3:美容贷 4:债权贷
            queryWrapper.and(w -> {
                // 只能判断后 通过标题模糊查询
                lendType.forEach(type -> {
                    switch (type) {
                        case 1:
                            w.or().like(Lend::getTitle, "车易贷");
                            break;
                        case 2:
                            w.or().like(Lend::getTitle, "房产贷");
                            break;
                        case 3:
                            w.or().like(Lend::getTitle, "美容贷");
                            break;
                        case 4:
                            w.or().like(Lend::getTitle, "债权贷");
                            break;
                    }
                });
            });

        }
        //判断标的还款方式 查询条件
        if (!CollectionUtils.isEmpty(returnMethod)) {
            //使用条件: 前端传入的值集合 和后端数据库还款方式值使用的对应
            queryWrapper.and(w -> w.in(Lend::getReturnMethod, returnMethod));
        }
        //判断标的还款期限查询条件
        if (!CollectionUtils.isEmpty(period)) {
            //使用条件:  1:1月以下  2:1-3月 3:3-6月 4:6-12月  5:12月及以上
            queryWrapper.and(w -> {
                period.forEach(p -> {
                    switch (p) {
                        case 1:
                            w.or().lt(Lend::getPeriod, 1);
                            break;
                        case 2:
                            w.or(q -> q.ge(Lend::getPeriod, 1).lt(Lend::getPeriod, 3));
                            break;
                        case 3:
                            w.or(q -> q.ge(Lend::getPeriod, 3).lt(Lend::getPeriod, 6));
                            break;
                        case 4:
                            w.or(q -> q.ge(Lend::getPeriod, 6).lt(Lend::getPeriod, 12));
                            break;
                        case 5:
                            w.or().ge(Lend::getPeriod, 12);
                            break;
                    }
                });

            });
        }
        //判断标的年利率 查询条件
        if (!CollectionUtils.isEmpty(lendYearRate)) {
            //使用条件: 1:12%以下  2:12%-14%  3:14%-16%  4:16%及以上
            queryWrapper.and(w -> {
                lendYearRate.forEach(l -> {
                    switch (l) {
                        case 1:
                            w.or().lt(Lend::getLendYearRate, 0.12);
                            break;
                        case 2:
                            w.or(q -> q.ge(Lend::getLendYearRate, 0.12).lt(Lend::getLendYearRate, 0.14));
                            break;
                        case 3:
                            w.or(q -> q.ge(Lend::getLendYearRate, 0.14).lt(Lend::getLendYearRate, 0.16));
                            break;
                        case 4:
                            w.or().ge(Lend::getLendYearRate, 0.16);
                            break;

                    }
                });

            });

        }
        //只查询募资中的标的
//        queryWrapper.eq(Lend::getStatus, 1);
        List<Lend> lends = this.list(queryWrapper);
        lends.forEach(lend -> {
            Integer status = lend.getStatus();
            String statusStr = getStatusString(status);
            lend.getParam().put("status", statusStr);
            String name = dictService.getDictNameByDictCodeAndValue("returnMethod", lend.getReturnMethod());
            lend.getParam().put("returnMethod", name);
        });

        return lends;
    }
    //放款：
    @Override
    public void makeLoan(String id) {
        Lend lend = this.getById(id);
        //1、访问hfb的放款接口： 在hfb中将投资人对本标的的投资转给借款人
        Map<String, Object> params = new HashMap<>();
        params.put("agentId", HfbConst.AGENT_ID);
        //agentProjectCode代表放款的标的的唯一编号
        params.put("agentProjectCode", lend.getLendNo());
        params.put("agentBillNo", LendNoUtils.getLoanNo());
        //计算手续费:  服务费月利率*期数*放款总金额
        //必须使用募资到的总金额来计算手续费
        BigDecimal mchFee = lend.getServiceRate().divide(new BigDecimal("12"), 8, BigDecimal.ROUND_DOWN)
                .multiply(new BigDecimal(lend.getPeriod()))
                .multiply(lend.getInvestAmount());
//                .multiply(lend.getAmount());
        params.put("mchFee", mchFee);//用户hfb账户得到的钱就是 放款总金额-手续费
        params.put("note", "" + lend.getTitle());
        params.put("timestamp", System.currentTimeMillis());
        String sign = RequestHelper.getSign(params);
        params.put("sign", sign);
        //放款的请求，JSONObject代表放款返回的结果
        JSONObject jsonObject = RequestHelper.sendRequest(params, HfbConst.MAKE_LOAD_URL);
        //2、验证hfb执行的结果
//        System.out.println(jsonObject.toJSONString());
        String resultCode = jsonObject.getString("resultCode");
        Assert.notTrue("0000".equals(resultCode), ResultCodeEnum.PAY_UNIFIEDORDER_ERROR);
        //3、更新标的状态
        lend.setStatus(2);//2代表还款中的状态
        lend.setPaymentTime(new Date());//放款时间
//        lend.setPaymentAdminId();  //放款的管理员id
        this.updateById(lend);
        //4、修改本地借款人
        //将hfb返回的 放款金额设置到借款人账户中
        BigDecimal voteAmt = jsonObject.getBigDecimal("voteAmt");
        Long userId = lend.getUserId();//借款人id
        userAccountService.updateUserAccount(voteAmt, userId);
        //保存借款人账户操作成功流水日志
        UserInfo userInfo = userInfoService.getById(userId);
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userId);
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(LendNoUtils.getNo());//直接保存成功的日志 无需回调来更新
        //（1：充值 2：提现 3：投标 4：投资回款 5: 账户入账...）
        transFlow.setTransType(5);
        transFlow.setTransTypeName("账户入账");
        transFlow.setTransAmount(voteAmt);
        transFlow.setMemo("借款人账户入账");
        transFlow.setStatus(1);
        transFlowService.save(transFlow);

        //5、投资人账户删除冻结金额中 本标的投资的金额
        //查询本标的所有成功的lend_item
        List<LendItem> lendItems = lendItemService.list(Wrappers.lambdaQuery(LendItem.class)
                .eq(LendItem::getLendId, lend.getId())
                .eq(LendItem::getStatus, 1));
        //遍历lend_item
        lendItems.forEach(lendItem -> {
            //通过lend_item中的投资人id查询他的账户 并用冻结金额-lend_item该用户的投资金额
            Long investUserId = lendItem.getInvestUserId();
            UserAccount investUserAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                    .eq(UserAccount::getUserId, investUserId));
            investUserAccount.setFreezeAmount(investUserAccount.getFreezeAmount()
                    .subtract(lendItem.getInvestAmount()));
            userAccountService.updateById(investUserAccount);
            //保存投资人账户操作流水日志
            UserInfo userInfo2 = userInfoService.getById(investUserId);
            TransFlow transFlow2 = new TransFlow();
            transFlow2.setUserId(investUserId);
            transFlow2.setUserName(userInfo2.getName());
            transFlow2.setTransNo(LendNoUtils.getNo());//直接保存成功的日志 无需回调来更新
            //（1：充值 2：提现 3：投标 4：投资回款 5: 账户入账 6:投资扣减...）
            transFlow2.setTransType(6);
            transFlow2.setTransTypeName("账户扣减");
            transFlow2.setTransAmount(lendItem.getInvestAmount());
            transFlow2.setMemo("投资账户扣减");
            transFlow2.setStatus(1);
            transFlowService.save(transFlow2);
        });
        //TODO: 生成还款计划和回款计划
        //还款计划
        List<LendReturn> lendReturns = lendReturnService.getLendReturns(lend);
        //将lendReturns转为期数映射lendReturn id的 map集合
        //生成回款计划
        List<LendItemReturn> allLendItemReturns = lendItemReturnService.getLendItemReturns(lend, lendReturns);
        //=================================所有的投资项 的回款计划全部生成了
        //遍历还款计划集合：为每一个还款计划设置 应还款本金 利息  本息
        lendReturns.forEach(lendReturn -> {
            //获取还款计划的期数
            Integer currentPeriod = lendReturn.getCurrentPeriod();
            //获取当前期所有的回款计划
            List<LendItemReturn> lendItemReturns = allLendItemReturns.stream().filter(lendItemReturn -> lendItemReturn.getCurrentPeriod() == currentPeriod)
                    .collect(Collectors.toList());
            //map(lendItemReturn -> lendItemReturn.getPrincipal()): 将集合转为 另一个类型的集合(lendItemReturn.getPrincipal()的集合)
            //.reduce((a, b) -> a.add(b)) 将前面的集合中的每两个元素累加
            BigDecimal totalPrincipal = lendItemReturns.stream().map(lendItemReturn -> lendItemReturn.getPrincipal())
                    .reduce((a, b) -> a.add(b))//.get()获取reduce计算的结果
                    .get();
            lendReturn.setPrincipal(totalPrincipal);
            BigDecimal totalInterest = lendItemReturns.stream().map(lendItemReturn -> lendItemReturn.getInterest())
                    .reduce((a, b) -> a.add(b))
                    .get();
            lendReturn.setInterest(totalInterest);
            BigDecimal total = lendItemReturns.stream().map(lendItemReturn -> lendItemReturn.getTotal())
                    .reduce((a, b) -> a.add(b))
                    .get();
            lendReturn.setTotal(total);
        });
        //批量更新所有的还款计划的 本金 利息 本息
        lendReturnService.updateBatchById(lendReturns);
    }





}
